本系列文章會在筆者的部落格繼續連載!Design System 101 感謝大家的閱讀!
深淺主題已經在前端社群風靡多年,小至個人部落格大至世界級客戶端應用 (Github),都有深淺色的切換功能,提供使用者可以自由地切換主題。
而要讓一個網站可以無痛的切換深淺色,最主要的原則就是在開發時遵循 Design Token,在先前 Token to CSS 的章節提到過其好處,如果色票沒有列在 Token 內則需要在應用層的 CSS 中去處理。
本篇會介紹如何透過 useTheme
去控制應用的深淺主題。
在最初的一開始我們介紹 Token 這個概念,透過 System Token (alias.json
) 的 light 與 dark 分別指向不同的 Ref Token (base.json
),這一步讓我們有能力可以讓同一個 System Token 的 alias 根據某些條件轉換成不同的值。
接著再透過 Design Token 建置時,將 :root
拆分成 light 與 dark 到 normalized.css
,並在網頁一開始載入該 CSS ,最後透過 data-theme
去控制要使用哪種主題色。
html[data-theme='light'],
.tocino-light {
--tocino-sys-color-primary: #6750a4;
// ... All System Token (淺色)
}
:root {
--tocino-sys-color-primary: #d0bcff;
// ... All System Token (深色)
}
接下來要實作的就是如何提供一個功能讓使用者可以切換 data-theme
的值,也就是本篇要介紹的 useTheme
。
useTheme
要做的事情很簡單,就是控制 data-theme
的值。
理想上我們希望當使用 useTheme
時可以讓它可以讓我們更新 data-theme
以及知道當前的 theme
import { useTheme } from '@tocino-ui/core/hooks';
export default () => {
const { theme, toggleTheme } = useTheme();
return (
<div>
<span>Current Theme: {theme}</span>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
};
名稱 | 型別 | 初始值 | 描述 |
---|---|---|---|
initialTheme | string | - | 初始的主題 |
名稱 | 型別 | 初始值 | 描述 |
---|---|---|---|
theme | string |
dark |
當前的主題 |
toggleTheme | () => void |
- | 切換主題 |
setTheme | React.SetStateAction<string> |
- | 設定主題 |
首先是 useTheme
的實作,會先透過 getTheme
取得 localStorage
的上次存取的 Theme, 如果沒有值,就將深色主題作為預設值,並且 mounting 的時候透過 useEffect
中去更新 data-theme
的值。最後將 theme
, toggleTheme
與 setTheme
這三個 API 透過 useMemo
傳遞給使用者。
而 themeManager
主要負責 localStorage
的存取與透過 documentElement.setAttribute
去更新 data-theme
的值。
/**
* ====== CONST ======
*/
const THEME_KEY = 'tocino-theme';
const THEME = {
LIGHT: 'light',
DARK: 'dark',
SYSTEM: 'system',
};
/**
* ====== THEME MANAGER ======
*/
const themeManager = {
getTheme: () => {
return window?.localStorage.getItem(THEME_KEY) ?? THEME.DARK;
},
setTheme: (theme) => {
document?.documentElement.setAttribute('data-theme', theme);
window?.localStorage.setItem(THEME_KEY, theme);
},
};
/**
* ====== `useTheme` ======
*/
function useTheme(initialTheme) {
const [theme, setTheme] = useState(() => {
return initialTheme ?? themeManager.getTheme(); // 取得 Default Theme
});
useEffect(() => {
themeManager.setTheme(theme);
}, [theme]);
const toggleTheme = useCallback((theme) => {
setTheme((prev) => (prev === THEME.DARK ? THEME.LIGHT : THEME.DARK));
}, []);
return useMemo(
() => ({
theme,
toggleTheme,
setTheme,
}),
[theme, toggleTheme],
);
}
可以看到上面引入了我們先前做的 Button 組件,並且透過 useTheme
去控制 data-theme
的值,這樣就可以讓我們的應用自由地切換深淺色主題。
明天見!